/*
 * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-12-09     Steven Liu   the first version
 * 2021-04-14     Meco Man     Check the file path's legitimacy of 'sy' command
 */

#include <rtthread.h>
#include <ymodem.h>
#include <stdlib.h>
#include <string.h>
#include "inflash.h"
#include "crc.h"

#define DFS_PATH_MAX 16

extern uint32_t ledFre ;
//extern uint32_t downLoadStartTick;
struct custom_ctx
{
    struct rym_ctx parent;
    int fd;
    int flen;
    char fpath[DFS_PATH_MAX];
    uint32_t appBaseAddr;
};

char* itoa(int num,char* str,int radix)
{/*索引表*/
    char index[]="0123456789ABCDEF";
    unsigned unum;/*中间变量*/
    int i=0,j,k;
    /*确定unum的值*/
    if(radix==10&&num<0)/*十进制负数*/
    {
        unum=(unsigned)-num;
        str[i++]='-';
    }
    else unum=(unsigned)num;/*其他情况*/
    /*转换*/
    do{
        str[i++]=index[unum%(unsigned)radix];
        unum/=radix;
       }while(unum);
    str[i]='\0';
    /*逆序*/
    if(str[0]=='-')
        k=1;/*十进制负数*/
    else
        k=0;
     
    for(j=k;j<=(i-1)/2;j++)
    {       char temp;
        temp=str[j];
        str[j]=str[i-1+k-j];
        str[i-1+k-j]=temp;
    }
    return str;
}

static enum rym_code _rym_recv_begin(
    struct rym_ctx *ctx,
    rt_uint8_t *buf,
    rt_size_t len)
{
    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
    ledFre = 50;
    cctx->fpath[0] = '/';
    // 不关心文件名
//    rt_strncpy(&(cctx->fpath[1]), (const char *)buf, DFS_PATH_MAX - 1);
    cctx->fd=1;
    if (cctx->fd < 0)
    {
//        rt_err_t err = rt_get_errno();
//        rt_kprintf("error creating file: %d\n", err);
        return RYM_CODE_CAN;
    }
    // 跳过第一个文件名字符串，就是文件长度字符串
    cctx->flen = atoi(1 + (const char *)buf + rt_strnlen((const char *)buf, len - 1));
    //擦除对应的flash区域，准备更新app
    if(cctx->flen >= 0x20000 * 4 || cctx->flen == 0)   // 超出了app的空间或者长度为0
    {
        return RYM_CODE_CAN;
    }
    // 把app页和标志页一起擦除
    InFlash_Erase(APP_ADDR,0x20000 * 5);

    cctx->appBaseAddr = APP_ADDR;
//    downLoadStartTick = rt_tick_get();
    return RYM_CODE_ACK;
}

static enum rym_code _rym_recv_data(
    struct rym_ctx *ctx,
    rt_uint8_t *buf,
    rt_size_t len)
{
    struct custom_ctx *cctx = (struct custom_ctx *)ctx;

    RT_ASSERT(cctx->fd >= 0);
    if (cctx->flen > 0)
    {
        int wlen = len > cctx->flen ? cctx->flen : len;
        InFLASH_Write(cctx->appBaseAddr,(uint32_t*)buf,wlen);
        cctx->flen -= wlen;
        cctx->appBaseAddr += wlen;
    }
        
    return RYM_CODE_ACK;
}

void Backup_App(void);
static enum rym_code _rym_recv_end(
    struct rym_ctx *ctx,
    rt_uint8_t *buf,
    rt_size_t len)
{
    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
    uint32_t updateSuccess = Calc_APP_Crc32();
    RT_ASSERT(cctx->fd >= 0);
    cctx->fd = -1;
    InFLASH_Write(APP_CRC32_ADDR,&updateSuccess,4);
    ledFre = 500;
    Backup_App();
//    downLoadStartTick = 0;
    return RYM_CODE_ACK;
}

static enum rym_code _rym_send_begin(
    struct rym_ctx *ctx,
    rt_uint8_t *buf,
    rt_size_t len)
{
    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
    char lenStr[16] = {0};
//    rt_err_t err;
//    uint32_t tag=0;
    uint32_t fLen = 0;
    uint8_t sector = 0;


    cctx->fd = 1;
    rt_memset(buf, 0, len);
    memcpy(buf,cctx->fpath,strlen(cctx->fpath)+1);
    //TODO  获取文件长度
    do
    {
        sector++;
        fLen += 128*1024;
    }while((*(uint32_t*)(APP_ADDR + sector*128*1024) != 0xFFFFFFFF) && sector < 6);       //以4个0xff判定结束，此方法不知是否妥当。
    cctx->flen = fLen;
    itoa(fLen,lenStr,10);
    lenStr[15] = 0;
    memcpy(buf+strlen(cctx->fpath)+1,lenStr,strlen(lenStr)+1);
    return RYM_CODE_SOH;
}

static enum rym_code _rym_send_data(
    struct rym_ctx *ctx,
    rt_uint8_t *buf,
    rt_size_t len)
{
//    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
    rt_size_t read_size;
    int retry_read;

    read_size = 0;
    for (retry_read = 0; retry_read < 10; retry_read++)
    {
//        read_size += read(cctx->fd, buf + read_size, len - read_size);
//        if (read_size == len)
//            break;
    }

    if (read_size < len)
    {
        rt_memset(buf + read_size, 0x1A, len - read_size);
        ctx->stage = RYM_STAGE_FINISHING;
    }

    return RYM_CODE_SOH;
}

static enum rym_code _rym_send_end(
    struct rym_ctx *ctx,
    rt_uint8_t *buf,
    rt_size_t len)
{
    rt_memset(buf, 0, len);

    return RYM_CODE_SOH;
}

struct custom_ctx customCtx;
uint8_t rymBuf[(1+2+1024+2)];
static rt_err_t rym_download_file(int idev)
{
    rt_err_t res;
    struct custom_ctx *ctx = &customCtx;
    memset(ctx,0,sizeof(struct custom_ctx));
    memset(rymBuf,0,sizeof(rymBuf));
    ctx->fd = -1;
    ctx->parent.buf = rymBuf;
    res = rym_recv_on_device(&ctx->parent, idev, 0,
                             _rym_recv_begin, _rym_recv_data, _rym_recv_end, 1024);
    if(res != RT_EOK)
    {
        // 如果没有成功下载，需要把用户区的数据擦除，否则不完整的固件会导致启动不了
        HAL_FLASH_Unlock();
        InFlash_Erase(ctx->appBaseAddr,ctx->flen);
        ledFre = 500;
    }

    return res;
}

static rt_err_t rym_upload_file(int idev, const char *file_path)
{
    rt_err_t res = 0;

    struct custom_ctx *ctx = &customCtx;
    memset(ctx,0,sizeof(struct custom_ctx));
    memset(rymBuf,0,sizeof(rymBuf));
    memset(ctx->fpath,0,sizeof(ctx->fpath));
    ctx->fd = -1;
    ctx->flen = 0;
    ctx->parent.buf = rymBuf;
    rt_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
    ctx->fpath[DFS_PATH_MAX-1] = 0;

    res = rym_send_on_device(&ctx->parent, idev,0,
                            _rym_send_begin, _rym_send_data, _rym_send_end, 1000);

    return res;
}

void Thread_Rym_Entry(void* parameter)
{
    while(1)
    {
        rym_download_file(1);
    }

}

